Thread: Incorrect initializing of a structure instance with data from const char *[][]

  1. #1
    Registered User
    Join Date
    Nov 2011
    Posts
    44

    Incorrect initializing of a structure instance with data from const char *[][]

    As the topic title says - I have a problem with
    error: initializer element is not constant
    The code:
    Code:
    #define LANG_EN 0
    #define LANG_PL 1
    #define LANGUAGES 2
    const char *const message[][LANGUAGES] = {
    
    
    #define MSG_INTRO 0
         /*0*/  { "Hello", "Witamy" },
    
    
    #define MSG_MENU1 1
         /*1*/  { "Option1", "Opcja1" },
    
    
    #define MSG_MENU2 2
         /*2*/  { "Option2", "Opcja2" },
    };
    Code:
    struct _menuitem
    {
        const char *const text;
        int action;
    };
    
    struct _menuitem const menu;
    Code:
    #define PGM_STR(X) ((const  char[]) { X })
    
    const struct _menuitem const menu = {PGM_STR(message[MSG_INTRO][LANG_PL]),  0};
    Code is divided in the same manner as source files are.

    When I put any string "Testest" under PGM_STR macro then everything is fine.

    What am I doing wrong? How should I initialize the struct with string data from
    Code:
    const char *const message[][LANGUAGES]

  2. #2
    Ticked and off
    Join Date
    Oct 2011
    Location
    La-la land
    Posts
    1,728
    You can declare a static const array of structures with const members. You want all those const's, if you want the data to be immutable (on a microcontroller, to go to flash/rom).

    For example:
    Code:
    #include <stdlib.h>
    #include <stdio.h>
    
    #define LANG_EN    0
    #define LANG_PL    1
    #define LANG_COUNT 2
    
    static const struct {
        const char *const message[LANG_COUNT];
        const int         action;
    } menu[] = {
        { { "Hello",   "Witamy" }, 3 },
        { { "Option1", "Opcja1" }, 1 },
        { { "Option2", "Opcja2" }, 2 },
        /* End of array marker; sentinel */
        { { NULL, NULL }, 0 }
    };
    
    int main(void)
    {
        int lang, i;
    
        for (lang = 0; lang < LANG_COUNT; lang++) {
            printf("lang = %d\n", lang);
    
            for (i = 0; menu[i].action != 0; i++)
                printf("menu[%d].action = %d, menu[%d].message[lang] = \"%s\"\n", i, menu[i].action, i, menu[i].message[lang]);
    
            printf("\n");
        }
    
        return EXIT_SUCCESS;
    }
    Questions?

  3. #3
    Registered User
    Join Date
    Nov 2011
    Posts
    44
    Thank you for your advice concerning array of structures.
    However, I would like to ask if it is possible to keep the initialization the way I have shown, one line per node. It was not shown in my code snippet, but the code structure is much more sophisticated, and as I assume the only way is to leave the initialization as I said in the previous sentence.
    Is it possible? I know I am having difficulty with all this const stuff. If needed I will use another macro/technique which would allow this to be operational.
    Any suggestions?

  4. #4
    Ticked and off
    Join Date
    Oct 2011
    Location
    La-la land
    Posts
    1,728
    Quote Originally Posted by Pole View Post
    Any suggestions?
    Most C compilers, GCC in particular, do not consider the address of a specific constant array element as a constant. (This sounds like a silly limitation, but I believe this is because the elements themselves do not have individual symbols at compile time. I am too lazy to check the C standards whether this is expected behaviour or a bug in the compiler, but I believe this is expected behaviour.)

    The workaround is to define each string as a separate variable (a static constant character array). Then, wherever you would use a string literal, you can just use the name of that variable instead. There is no run-time overhead, either. (In fact, this technique often saves run-time read-only memory, if you use the same string repeatedly. Many compilers store the same exact string multiple times, if defined as string literal multiple times. This technique avoids that, too.)

    Here is an example:
    Code:
    #include <stdlib.h>
    #include <stdio.h>
    
    #define ENGLISH   0
    #define POLISH    1
    #define LANGUAGES 2
    
    
    #define DEFINE_MESSAGE(name, english, polish) \
        static const char msg_en_ ## name [] = english, \
                          msg_pl_ ## name [] = polish
    
    #define MESSAGES(name) \
        { msg_en_ ## name, msg_pl_ ## name }
    
    #define ENGLISH_MESSAGE(name) \
        msg_en_ ## name
    
    #define POLISH_MESSAGE(name) \
        msg_pl_ ## name
    
    /* The following three DEFINE_MESSAGES() lines are equivalent to
     *      static const char msg_en_intro[] = "Hello";
     *      static const char msg_pl_intro[] = "Witamy";
     *      static const char msg_en_menu1[] = "Option1";
     *      static const char msg_pl_menu1[] = "Opcja1";
     *      static const char msg_en_menu2[] = "Option2";
     *      static const char msg_pl_menu2[] = "Opcja2";
    */
    DEFINE_MESSAGE(intro, "Hello", "Witamy");
    DEFINE_MESSAGE(menu1, "Option1", "Opcja1");
    DEFINE_MESSAGE(menu2, "Option2", "Opcja2");
    
    
    /* This is how to define the original messages array using the above.
     * Note, however, that you are unlikely to actually need this array.
    */
    static const char *const message[][LANGUAGES] = {
    
    #define MSG_INTRO 0
        /* 0 */ MESSAGES(intro),
    
    #define MSG_MENU1 1
        /* 1 */ MESSAGES(menu1),
    
    #define MSG_MENU2 2
        /* 2 */ MESSAGES(menu2),
    
    #define MSG_COUNT 3
    };
    
    
    /* This is a structure that describes one menu entry,
     * with text in each language.
    */
    struct menuitem {
        const char *const message[LANGUAGES];
        const int         action;
    };
    
    static const struct menuitem  menu = { MESSAGES(intro), 1 };
    
    
    /* This structure contains only one language text.
    */
    struct langmenu {
        const char *const message;
        const int         action;
    };
    
    static const struct langmenu  menu2 = { POLISH_MESSAGE(menu2), 2 };
    
    
    int main(void)
    {
        int msg, lang;
    
        for (msg = 0; msg < MSG_COUNT; msg++)
            for (lang = 0; lang < LANGUAGES; lang++)
                printf("message[%d][%d] = \"%s\"\n", msg, lang, message[msg][lang]);
    
        printf("menu.message[ENGLISH] = \"%s\"\n", menu.message[ENGLISH]);
        printf("menu.message[POLISH] = \"%s\"\n", menu.message[POLISH]);
        printf("menu.action = %d\n", menu.action);
    
        printf("menu2.message = \"%s\"\n", menu2.message);
        printf("menu2.action = %d\n", menu.action);
    
        printf("ENGLISH_MESSAGE(menu1) = \"%s\"\n", ENGLISH_MESSAGE(menu1));
    
        return EXIT_SUCCESS;
    }
    In short, the above code uses the preprocessor macros to shorten the variable definitions. Each string is contained in a separate variable, but because the variable names use a simple logic, the MESSAGES() macro can construct an array of pointers to the desired string in each language. Further languages are quite easy and straightforward to add, if desired.

    The ENGLISH_MESSAGE() and POLISH_MESSAGE() macros are a convenience, so that you do NOT need to try and remember how the original string variable names are constructed. You should always use these, so that if there is a namespace collision (that is, the variable names clash with ones you'd like to use), you can simply change how the string variables are named.

    Questions?

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Replies: 4
    Last Post: 08-14-2012, 03:31 PM
  2. Replies: 4
    Last Post: 04-20-2011, 01:19 PM
  3. Replies: 3
    Last Post: 11-15-2009, 04:57 AM
  4. Structure tags and instance
    By cjohnman in forum C Programming
    Replies: 2
    Last Post: 05-01-2008, 09:52 AM
  5. initializing a const ptr via another ptr
    By nuuphonic in forum C Programming
    Replies: 6
    Last Post: 04-19-2005, 11:33 AM